// SPDX-FileCopyrightText: Adam Evyčędo
//
// SPDX-License-Identifier: AGPL-3.0-or-later

package traffic

import (
	"fmt"
	"os"
	"path/filepath"

	"git.sr.ht/~sircmpwn/go-bare"
	"notabug.org/apiote/gott"
)

func openTrips(context Context) (*os.File, error) {
	fileName := filepath.Join(context.DataHome, context.FeedID, string(context.Version), "trips.bare")
	return os.Open(fileName)
}

func GetTripsByOffset(offsets []uint, context Context, filter func(Trip) bool) (map[uint]Trip, error) {
	trips := map[uint]Trip{}
	file, err := os.Open(filepath.Join(context.DataHome, context.FeedID, string(context.Version), "trips.bare"))
	if err != nil {
		return trips, fmt.Errorf("while opening file: %w", err)
	}
	defer file.Close()

	offsetsSet := map[uint]struct{}{}
	for _, offset := range offsets {
		offsetsSet[offset] = struct{}{}
	}

	for offset := range offsetsSet {
		_, err = file.Seek(int64(offset), 0)
		if err != nil {
			return trips, fmt.Errorf("while seeking to %d: %w", offset, err)
		}
		trip := Trip{}
		err = bare.UnmarshalReader(file, &trip)
		if err != nil {
			return trips, fmt.Errorf("while unmarshalling at %d: %w", offset, err)
		}
		if filter(trip) {
			trips[offset] = trip
		}
	}
	return trips, nil
}

func GetTripByOffset(file *os.File, offset uint, context Context) (Trip, error) {
	result := _Result{
		file:   file,
		Offset: offset,
	}
	r, e := gott.NewResult(result).
		Bind(seek).
		Bind(unmarshalTrip).
		Finish()
	if e != nil {
		return Trip{}, e
	} else {
		return r.(_Result).Trip, nil
	}
}

func GetTrips(ids []string, ctx Context, traffic *Traffic) (map[string]Trip, error) {
	trips := map[string]Trip{}
	file, err := openTrips(ctx)
	if err != nil {
		return trips, fmt.Errorf("while opening trips: %w", err)
	}
	defer file.Close()
	tripOffsets := map[string]struct{}{}
	for _, id := range ids {
		tripOffsets[id] = struct{}{}
	}
	tripIndex := traffic.TripIndexes[ctx.FeedID][ctx.Version]
	for _, o := range tripIndex {
		if _, ok := tripOffsets[o.Name]; ok {
			trip, err := GetTripByOffset(file, o.Offsets[0], ctx)
			if err != nil {
				return trips, fmt.Errorf("while getting trip %s: %w", o.Name, err)
			}
			trips[trip.Id] = trip
		}
	}
	return trips, nil
}

func GetTrip(file *os.File, id string, context Context, traffic *Traffic) (Trip, error) {
	tripIndex := traffic.TripIndexes[context.FeedID][context.Version]
	for _, o := range tripIndex {
		if o.Name == id {
			return GetTripByOffset(file, o.Offsets[0], context)
		}
	}
	return Trip{}, fmt.Errorf("trip by id %s not found", id)
}

func unmarshalDepartures(input ...interface{}) (interface{}, error) {
	result := input[0].(_Result)

	result.DeparturesSchedule = []Departure{}
	err := bare.UnmarshalReader(result.file, &result.DeparturesSchedule)
	result.file.Close()
	return result, err
}
